module Parse where

import Prelude hiding (between)

import AST (Instr(..), Oper(..), Proc)
import Control.Alt ((<|>))
import Control.Lazy (defer)
import Data.Either (Either(..))
import Data.List (catMaybes)
import Data.Map as M
import Data.Maybe (Maybe(..))
import Data.String (Pattern(..), contains)
import Data.String.CodeUnits as C
import Effect (Effect)
import Effect.Exception (throw)
import Parsing (ParseError(..), Parser, Position(..), runParser)
import Parsing.Combinators (between, choice, many, skipMany1)
import Parsing.String (char, eof, satisfy)

procP :: Parser String Proc
procP = catMaybes <$> many instr
  where
  instr = (Just <$> (single <|> loop)) <|> ws
  single = choice
    [ char '>' $> Move 1
    , char '<' $> Move (-1)
    , char '+' $> Update (M.singleton 0 (Add 1))
    , char '-' $> Update (M.singleton 0 (Add (-1)))
    , char '.' $> Out 0
    , char ',' $> In 0
    ]
  loop = defer \_ -> do
    proc <- between (char '[') (char ']') procP
    pure $ Loop { idx: 0, proc }
  ws = skipMany1 (satisfy (notChar "<>+-.,[]")) $> Nothing
  notChar s c = not $ contains (Pattern (C.singleton c)) s

parser ∷ Parser String Proc
parser = procP <* eof

parse ∷ String -> Effect Proc
parse s = case runParser s parser of
  Left (ParseError err (Position { line, column })) -> throw
    $ "Error at line "
        <> show line
        <> ", column "
        <> show column
        <> ":\n"
        <> err
  Right x -> pure x
